package com.example.websocket.server;

import com.alibaba.fastjson2.JSON;
import com.example.websocket.config.ResultVo;
import com.example.websocket.config.SocketConstant;
import com.example.websocket.config.SysUser;
import io.micrometer.common.util.StringUtils;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket服务端2
 *
 * @author raos
 * @date 2024-03-04 17:37
 */
@ServerEndpoint("/ws/{token}")
@Component
@Slf4j
public class WebSocketServer2 {

    /*** 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的 */
    private static int onlineCount = 0;

    /*** concurrent包的线程安全集合，也可以map改成set，用来存放每个客户端对应的Session对象 */
    private static final Map<String, Session> webSocketMap = new ConcurrentHashMap<>();

    /*** token用户映射 */
    private static final Map<String, SysUser> tokenUserMap = new ConcurrentHashMap<>();

    public static Map<String, Session> getWebSocketMap() {
        return webSocketMap;
    }

    public static Map<String, SysUser> getTokenUserMap() {
        return tokenUserMap;
    }

    /*** 连接建立成功调用的方法 */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        try {
            // 解析token判断用户登录状态，正常就保存用户信息
            SysUser sysUser = parseToken(session, token);
            if (ObjectUtils.isEmpty(sysUser)) {
                session.close();
                return;
            }
            /* 接收相同用户的不同会话(同一账号不同PC登录的情形) */
            if (!webSocketMap.containsKey(token)) {
                // 加入集合中
                webSocketMap.put(token, session);
                // 在线数加1
                addOnlineCount();
            }
            sendMessage(session,"连接成功！");
            log.info("用户连接:{},当前会话:{},当前在线人数为:{}", sysUser.getUsername(), session.getId(), getOnlineCount());
        } catch (IOException e) {
            log.error("当前会话:" + session.getId() + ",网络异常!!!!!!");
            log.error("", e);
        }
    }

    /*** 连接关闭调用的方法 */
    @OnClose
    public void onClose(Session session) throws IOException {
        String token = getTokenBySession(session);
        if (StringUtils.isNotBlank(token) && webSocketMap.containsKey(token)) {
            webSocketMap.remove(token);
            tokenUserMap.remove(token);
            // 从集合中删除
            subOnlineCount();
        }
        log.info("用户退出,当前会话:"+ session.getId() + ",当前在线人数为:" + getOnlineCount());
        session.close();
    }

    /***
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("【websocket消息】收到客户端发来的消息:{}", message);
        try {
            // 根据session获取token以及用户信息
            String token = getTokenBySession(session);
            if (StringUtils.isNotBlank(token)) {
                SysUser user = tokenUserMap.get(token);
                // 模拟接收客户端消息后发送业务
                WebSocketServer2.sendInfo(message, user.getUsername(), session);
            }
        } catch (IOException e) {
            log.error("收到客户端消息", e);
        }
    }

    /***
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        try {
            log.error("会话错误,当前会话:"+ session.getId() + ",原因:" + error.getMessage());
            log.error("", error);
            ResultVo<Object> resultVo = ResultVo.error(SocketConstant.GENERIC_MSG_TYPE, "会话错误！" + error.getMessage());
            sendMessage(session, JSON.toJSONString(resultVo));
        } catch (IOException e) {
            log.error("", e);
        }
    }

    /*** 根据Session获取token */
    public static String getTokenBySession(Session session) {
        List<Map.Entry<String, Session>> list = webSocketMap.entrySet().stream()
                .filter(entry -> entry.getValue().getId().equals(session.getId())).toList();
        if (!CollectionUtils.isEmpty(list)) {
            return list.get(0).getKey();
        }
        return null;
    }

    /*** 解析token，正常就保存，错误就返回消息 */
    private SysUser parseToken(Session session, String token) throws IOException {
        if (StringUtils.isBlank(token)) {
            ResultVo<Object> resultVo = ResultVo.error(SocketConstant.GENERIC_MSG_TYPE, "token参数不存在！");
            String msg = JSON.toJSONString(resultVo);
            log.error(msg);
            sendMessage(session, msg);
            return null;
        }
        // 解析token
        SysUser user = doParseToken(token);
        if (ObjectUtils.isEmpty(user)) {
            ResultVo<Object> resultVo = ResultVo.error(SocketConstant.GENERIC_MSG_TYPE, "token无效！");
            String msg = JSON.toJSONString(resultVo);
            log.error(msg);
            sendMessage(session, msg);
            return null;
        }
        // 保存用户信息
        tokenUserMap.put(token, user);
        return user;
    }

    /*** 模拟实际的解析 */
    private SysUser doParseToken(String token) {
        long millis = System.currentTimeMillis();
        // token有效
        if (millis % 3 == 0) {
            SysUser user = new SysUser();
            user.setUserId(System.currentTimeMillis());
            user.setUsername(token);
            return user;
        }
        return null;
    }

    /*** 实现服务器主动推送 */
    public static void sendMessage(Session session, String message) throws IOException {
        session.getBasicRemote().sendText(message);
    }

    /*** 发送自定义消息 */
    public static void sendInfo(String message, String username, Session session) throws IOException {
        log.info("发送消息到:" + username + ",当前会话:"+ session.getId() + "，报文:" + message);
        String token = getTokenBySession(session);
        if (StringUtils.isNotBlank(token) && webSocketMap.containsKey(token)) {
            sendMessage(session, message);
        } else {
            log.error("用户" + username + ",当前会话:"+ session.getId() + ",不在线！");
        }
    }

    public static void sendInfo(String message, String username) {
        try {
            List<String> list = getTokensByUsername(username);
            for (String s : list) {
                sendMessage(webSocketMap.get(s), message);
            }
        } catch (Exception e) {
            log.error("主动推送消息失败", e);
        }
    }

    private static List<String> getTokensByUsername(String username) {
        return tokenUserMap.entrySet().stream()
                .filter(e -> e.getValue().getUsername().equals(username))
                .map(Map.Entry::getKey).toList();
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer2.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer2.onlineCount--;
    }

}
